package com.nulldev.util.osAPI.hardware.cpu;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.osAPI.OS;

public class CPU {
	private static final Logger log = LoggerFactory.getLogger();

	private Vendor vendor;
	private Brand brand;
	private String data;
	private String[] array;

	public CPU(final Vendor vendor, final String data) {
		this.data = data;
		this.array = data.split(" ");
		this.vendor = vendor;
		this.brand = Brand.UNKNOWN;
	}

	public String getCPUData() {
		return this.data;
	}

	public Vendor getVendor() {
		return this.vendor;
	}

	public VMVendor getVMVendor() {
		if (!isVM()) {
			return VMVendor.NOT_VM;
		}
		return VMVendor.get(System.getenv("PROCESSOR_IDENTIFIER"));
	}

	public Brand getBrand() {
		if (this.vendor == Vendor.AMD && OS.isWindows()) {
			if (StringsUtil.isEqual(this.array, 1, "Family")) {
				final int val = MathUtil.safeParseList(this.array, 2, -1);
				if (val != -1) {
					if (data.contains("AMD A") && data.contains("APU")) {
						return Brand.fromID(-val);
					} else {
						return Brand.fromID(val);
					}
				}
			}
		} else if (this.vendor == Vendor.AMD && OS.isLinux()) {
			for (final String entry : this.array) {
				final String[] val = entry.split(":");
				if (val[0].equals("cpu family")) {
					final int x = MathUtil.safeParseInt(val[1].substring(1), -1);
					if (x != -1) {
						if (data.contains("AMD A") && data.contains("APU")) {
							return Brand.fromID(-x);
						} else {
							return Brand.fromID(x);
						}
					}
				}
			}
		} else if (this.vendor == Vendor.INTEL) {
			for (int idx = 0; idx < this.array.length; idx++) {
				final String entry = this.array[idx];
				final String[] val = entry.split(":");
				if (val[0].equals("cpu family")) {
					final int x = MathUtil.safeParseInt(val[1].substring(1), -1);
					return Brand.fromID(x);
				} else if (val[0].equals("Family")) {
					final int x = MathUtil.safeParseInt(this.array[idx + 1], -1);
					return Brand.fromID(x);
				}
			}
		}
		return this.brand;
	}

	public boolean isVM() {
		return this.vendor == Vendor.VIRTUAL_MACHINE;
	}

	public String arch() {
		return System.getenv("PROCESSOR_ARCHITECTURE");
	}

	public boolean isX86() {
		if (this.arch() != null) {
			return this.arch().equals("x86");
		} else {
			return true;
		}
	}

	/**
	 * ONLY USED BY parse()
	 */
	protected void override_data(final String[] data) {
		if (!Thread.currentThread().getStackTrace()[2].getMethodName().equals("parse")) {
			log.warn("Unsafe function called!");
		}
		this.array = data;
	}

	@Override
	public String toString() {
		final StringBuilder out = new StringBuilder(24);
		out.append("CPU[cpu=" + StringsUtil.cleanInvalidCharacters(this.data) + ",vendor=" + this.vendor.displayName() + ",brand="
				+ this.getBrand().displayName() + ",amd64=" + this.isAMD64() + ",virtual_machine=" + this.isVM() + "]");
		return out.toString();
	}

	private boolean __isAMD64() {
		if (this.vendor == Vendor.AMD || this.vendor == Vendor.AMD_ALT) {
			return StringsUtil.isEqual(this.array, 0, "AMD64") || ArrayUtils.contains(getArch().getSearch(), "amd64")
					|| System.getProperty("sun.cpu.isalist").contains("amd64");
		} else if (this.vendor == Vendor.INTEL) {
			return StringsUtil.isEqual(this.array, 0, "Intel64") || ArrayUtils.contains(getArch().getSearch(), "intel64")
					|| System.getProperty("sun.cpu.isalist").contains("amd64");
		}
		return false;
	}

	public boolean isAMD64() {
		return this.is64Bit;
	}

	public boolean isCVE_2018Safe() {
		if (this.vendor == Vendor.AMD || this.vendor == Vendor.VIA || this.vendor == Vendor.VIRTUAL_MACHINE) {
			return true;
		}
		return false;
	}

	public void println() {
		JVM.println(this.toString());
	}

	private boolean is64Bit = __isAMD64();

	public void override64(final boolean _64bit) {
		this.is64Bit = _64bit;
	}

	public Arch getArch() {
		return Arch.getArch();
	}
}
